/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package analisis;

import java.util.List;
import java.util.Stack;

import reticulado.Estado;
import reticulado.PointsToGraph;
import soot.Body;
import soot.Local;
import soot.PhaseOptions;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.toolkits.callgraph.CHATransformer;
import soot.options.Options;
import soot.tagkit.LineNumberTag;
import soot.toolkits.graph.BriefUnitGraph;
import soot.toolkits.graph.UnitGraph;

/**
 *
 * @author Santiago Avendaño
 */
public class InterproceduralKInfiniteAnalysis {

    private PointsToGraph resultado;
    private SootMethod metodoPrincipal;
    private Stack<String> callString;

    public InterproceduralKInfiniteAnalysis(String nombreClase, String nombreMetodo) {
    	callString = new Stack<String>();
        Options.v().set_keep_line_number(true);
        PhaseOptions.v().setPhaseOption("jb", "use-original-names:true");
        SootClass c = Scene.v().loadClassAndSupport(nombreClase);
        c.setApplicationClass();
        resultado = doAnalysis(nombreClase, nombreMetodo);
    }

    /*
     * Metodo que lanza el analisis interprocedural en una clase
     * La clase debe tener al menos un metodo estático desde
     * donde se deben llamar a los metodos que se desean analizar.
     */
    private PointsToGraph doAnalysis(String nombreClase, String nombreMetodo) {
        //System.out.println("Analizando clase " + nombreClase);
        Options.v().set_main_class(nombreClase);
        SootClass c = Scene.v().loadClassAndSupport(nombreClase);
        c.setApplicationClass();

        CHATransformer.v().transform();

        metodoPrincipal = c.getMethodByName(nombreMetodo);
//        Collection<SootMethod> metodosAanalizar = new LinkedList();
//        // Busco metodos estatico
//        for (SootMethod method : c.getMethods()) {
//            if (method.isStatic()) {
//                metodosAanalizar.add(method);
//            }
//        }
//        if (metodosAanalizar.isEmpty()) {
//            System.out.println("no se encontraron metodos estaticos en la clase " + c.getName());
//        } else {
//            metodoPrincipal = metodosAanalizar.iterator().next();
//        }
    	callString.push(metodoPrincipal.getDeclaringClass().getName() + "." + metodoPrincipal.getName());
    	System.out.println(callString);
        Body b = metodoPrincipal.retrieveActiveBody();
        UnitGraph g = new BriefUnitGraph(b);
        IntraProceduralAnalysis an = new IntraProceduralAnalysis(g, this, null, null, callString);
        PointsToGraph res = an.getResult();
        callString.pop();
        return res;
    }

    // out e in traen un mismo ptg copiado
    public void analyseCall(PointsToGraph in, InvokeStmt stmt, PointsToGraph out) {
        Estado estadoInicial = in.getEstado();
        InvokeExpr callExpr = stmt.getInvokeExpr();

        //Armo el mapping de argumentos con parametros
        List<Local> argumentos = callExpr.getArgs();
        Mapping mapping = new Mapping(argumentos.size());
        int i = 0;
        for (Local arg : argumentos) {
            mapping.setearParametro(i, in.alcanzados(arg));
            i++;
        }
        
        Local base = null;
        if (!(stmt instanceof StaticInvokeExpr)) {
            base = (Local) ((InstanceInvokeExpr) callExpr).getBase();
            mapping.setearThis(in.alcanzados(base));
        }

        out.setEstado(new Estado());
        SootMethod m = callExpr.getMethod();
        Body b = m.retrieveActiveBody();
        UnitGraph g = new BriefUnitGraph(b);
        //pusheo en la pila el nombre del metodo que vamos a analizar junto con la linea donde fue llamado
		LineNumberTag tag = (LineNumberTag) stmt.getTag("LineNumberTag");
    	callString.push(tag + ":" + m.getDeclaringClass().getName() + "." + m.getName());
        if (callString != null) {
            for (String call : callString) {
            	System.out.println(call);
            }
        }
        //le paso el grafo inicial pero quitandole las varibles locales
        IntraProceduralAnalysis an = new IntraProceduralAnalysis(g, this, out, mapping, callString);
        // guardo el resultado en out
        an.copyResult(out);
        //vuelvo el estado de las variables al estado inicial
        out.setEstado(estadoInicial);
        //popeo de la pila el metodo ya analizado
        callString.pop();
    }

    public PointsToGraph getResult() {
        return resultado;
    }

    public SootMethod getMetodoPrincipal() {
        return metodoPrincipal;
    }
}
